// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef SRC_WORKER_H_
#define SRC_WORKER_H_

#include <pthread.h>
#include <map>
#include <deque>
#include <string>

#include "connection.h"
#include "proto/seven_summits.pb.h"

namespace seven_summits {

// Abstract class that encapsulates the actual processing. Each worker
// Each worker has a thread and a queue or pending work items.
class Worker : public Connection::ProcessingCallback {
 public:
  Worker()
      : work_items_() {
    pthread_mutex_init(&cond_mutex_, NULL);
    pthread_cond_init(&cond_, NULL);
    pthread_mutex_init(&work_items_mutex_, NULL);
  }
  virtual ~Worker() {
    pthread_mutex_destroy(&cond_mutex_);
    pthread_cond_destroy(&cond_);
    pthread_mutex_destroy(&work_items_mutex_);
  }

  const SmtRequest& request() { return *work_items_.front().request; }
  SmtResponse* response() { return work_items_.front().response; }
  Connection* connection() { return work_items_.front().connection; }

  // Returns true if init was successful.
  bool Init();

  // ProcessingCallback implementation. Puts a work item in the queue.
  virtual void ProcessAsync(const SmtRequest& request, SmtResponse* response,
                            Connection* connection);

  // Actual solver implementation.
  virtual void Solve() = 0;

 private:
  friend void* call_loop(void*);
  struct WorkItem {
    const SmtRequest* request;
    SmtResponse* response;
    Connection* connection;
  };

  // Returns true if work_items_ is empty.
  bool HasEmptyWorkQueue();

  // Waits until !HasEmptyWorkQueue() then processes the front.
  void* ProcessingLoop();

  pthread_t thread_;
  pthread_mutex_t cond_mutex_;
  pthread_cond_t cond_;

  std::deque<WorkItem> work_items_;
  pthread_mutex_t work_items_mutex_;
};

class WorkerFactory {
 public:
  class Registerer {
   public:
    Registerer(const std::string& name, WorkerFactory* factory) {
      WorkerFactory::Register(name, factory);
    }
  };

  WorkerFactory() {}
  virtual ~WorkerFactory() {}

  // Constructs an actual worker. Implementations make themselves
  // known to the service by registering a factory.
  virtual Worker* ConstructWorker() = 0;

  static void Register(const std::string& name, WorkerFactory*);

  // Returns a factory instance registered by name.
  static WorkerFactory* GetByName(const std::string& name);

 private:
  static void InitRegistry();

  static pthread_once_t module_init_;
  static std::map<std::string, WorkerFactory*>* factory_map_;
};
}  // namespace seven_summits

#endif  // SRC_WORKER_H_
